// (c) 2008 Kickstone Technology Ltd

#include "stdafx.h"
#include "ButtonDemoBHO.h"
#include "IEWindow.h"
#include "HtmlWindowEventSink.h"
#include "WebBrowserEventSink.h"


const int BUTTON_PRESSED	= 1;
const int MENU_ITEM_SELECT	= 2;

// defined in the new windows sdk
const int OLECMDIDF_WINDOWSTATE_USERVISIBLE	= 0x00000001;
const int OLECMDIDF_WINDOWSTATE_ENABLED		= 0x00000002;



HRESULT CButtonDemoBHO::FinalConstruct(){
	// set up the multiple thread stuff
	m_spTable.CoCreateInstance(CLSID_StdGlobalInterfaceTable);
	m_interfaceTableCookie=0;
	m_threadID=0;
	m_htmlWindowEventSink=NULL;
	m_hWnd=NULL;
	m_ieWindow=NULL;
	m_browserEventSink=new CWebBrowserEventSink(this);
	return S_OK;
}


void CButtonDemoBHO::FinalRelease() {
	if (m_interfaceTableCookie){
		m_spTable->RevokeInterfaceFromGlobal(m_interfaceTableCookie);
		m_interfaceTableCookie=0;
	}

	if (m_browserEventSink)	// just in case
		delete m_browserEventSink;

}



///////////////////////////////////////////////////////////////////////////
// IWindowHandlerCallback functions
///////////////////////////////////////////////////////////////////////////

// release the windowhandler pointer
void CButtonDemoBHO::releasePointer(){
	m_htmlWindowEventSink=NULL;
}

// document has completed loading - should be called by refresh actions
void CButtonDemoBHO::onLoadOccured(){
	ATLTRACE("CButtonDemoBHO::onLoadOccured\n");
	// refresh has completed
	MessageBox(m_hWnd, L"Main Document has been refreshed", L"Refresh Complete", MB_OK);
}


///////////////////////////////////////////////////////////////////////////
// DWebBrowserEvents2 event Handlers
///////////////////////////////////////////////////////////////////////////

// Called just before navigation to a page begins
HRESULT CButtonDemoBHO::onBeforeNavigate2(IDispatch *pDisp, VARIANT *vUrl, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers, VARIANT_BOOL *Cancel){
	ATLTRACE("CButtonDemoBHO::onBeforeNavigate2 %S\n", vUrl->bstrVal);
	if (isMainFrame(pDisp)){
		ATLTRACE("Main Document\n");
		m_normalPageLoad=true;
	}
	return S_OK;
}

// Called just after navigation to a page completes
HRESULT CButtonDemoBHO::onNavigateComplete2(IDispatch *pDisp, VARIANT *vUrl){
	ATLTRACE("CButtonDemoBHO::onNavigateComplete2 %S\n", vUrl->bstrVal);
	if (isMainFrame(pDisp)){
		ATLTRACE("Main Document\n");

		m_lastURL=vUrl->bstrVal;
	}
	return S_OK;
}

// Called when a page is about to be downloaded
// used to track page refreshes
HRESULT CButtonDemoBHO::onDownloadBegin(){
	ATLTRACE("CButtonDemoBHO::onDownloadBegin\n");
	if (!m_normalPageLoad){
		bool isRefresh=true;
		CComPtr<IDispatch> spDisp;
		HRESULT hr=m_spWebBrowser2->get_Document(&spDisp);
		if (SUCCEEDED(hr) && spDisp){
			ATLTRACE("---Refresh - got document\n");
			CComQIPtr<IHTMLDocument2> spHtmlDoc=spDisp;
			if (spHtmlDoc){
				ATLTRACE("---Refresh - got html document\n");
				CComBSTR state;
				hr=spHtmlDoc->get_readyState(&state);
				if (SUCCEEDED(hr) && state==L"complete")
					isRefresh=false;					
			}
		}
		// not normal, so looks like we've got a refresh here
		if (!m_htmlWindowEventSink && isRefresh){
			ATLTRACE("---got Refresh\n");
			setWindowHandler();
			MessageBox(m_hWnd, L"Main Document is being refreshed", L"Refresh Started", MB_OK);

		}
	}
	return S_OK;
}

HRESULT CButtonDemoBHO::onDocumentComplete(IDispatch *pDisp, VARIANT *vUrl){
	ATLTRACE("CButtonDemoBHO::onDocumentComplete %S\n", vUrl->bstrVal);
	if (isMainFrame(pDisp)){
		m_normalPageLoad=false;
		MessageBox(m_hWnd, L"Main Document has completed loading", L"Document Complete", MB_OK);
		return S_OK;
	}
	return S_OK;
}

// Called when the window/frame is about to quit
// we need to clean up everything here
HRESULT CButtonDemoBHO::onQuit(){
	m_ieWindow->removeBHO();
	return S_OK;
}

// Called when the tab state changes
HRESULT CButtonDemoBHO::onWindowStateChanged(DWORD dwFlags, DWORD dwValidFlagsMask){
	bool userVisible=false;
	if ((dwValidFlagsMask & OLECMDIDF_WINDOWSTATE_USERVISIBLE) == OLECMDIDF_WINDOWSTATE_USERVISIBLE){
		userVisible=((dwFlags & OLECMDIDF_WINDOWSTATE_USERVISIBLE) == OLECMDIDF_WINDOWSTATE_USERVISIBLE);
		ATLTRACE("CButtonDemoBHO::onWindowStateChanged %d\n", userVisible);
		if (userVisible){
			// Set us as the active BHO
			m_ieWindow->setCurrentBHO(this);
		} else
			// Clear us as the active BHO
			m_ieWindow->setCurrentBHO(NULL);
	}
	return S_OK;
}

///////////////////////////////////////////////////////////////////////////
// IObjectWithSite function
///////////////////////////////////////////////////////////////////////////

// Store the IWebBrowser ptr and setup the BHO
STDMETHODIMP CButtonDemoBHO::SetSite(IUnknown *pUnkSite) {
	// Called by IE just after the BHO created
	if (!pUnkSite)
		ATLTRACE("\nCButtonDemoBHO::SetSite(): pUnkSite is NULL\n\n");
	else {
		// Query pUnkSite for the IWebBrowser2 interface.
		m_spWebBrowser2 = pUnkSite;
		if (m_spWebBrowser2) {

			// register this in the global interface table
			HRESULT hr=m_spTable->RegisterInterfaceInGlobal((IOleCommandTarget *)this, IID_IOleCommandTarget, &m_interfaceTableCookie);
			if (FAILED(hr))
				return S_OK;
			// get the current thread id 
			m_threadID=GetCurrentThreadId();

			getHWND();
			// set up the webBrowser event handler
			if (!m_browserEventSink->advise(m_spWebBrowser2))
				return S_OK;	// bail if we can't create the connection

			// add to list of known windows and get pointer to CIEWindow class
			m_ieWindow=CIEWindow::addWindow(m_hWnd);
			m_ieWindow->addBHO();
			m_ieWindow->setCurrentBHO(this);
		}
	}
	return S_OK;
}


///////////////////////////////////////////////////////////////////////////
// Internal member functions
///////////////////////////////////////////////////////////////////////////

// get the HWND of the main browser window
void CButtonDemoBHO::getHWND(){
	SHANDLE_PTR nBrowser = NULL;
	HRESULT hr=m_spWebBrowser2->get_HWND(&nBrowser);
	m_hWnd = (HWND)nBrowser;
}

// clear the browser event handler pointer
void CButtonDemoBHO::releaseBrowserSink(){
	m_browserEventSink=NULL;
}

// check if this is the main frame of the document
bool CButtonDemoBHO::isMainFrame(IDispatch *pDisp){
	CComQIPtr<IWebBrowser2> spTempWebBrowser = pDisp;
	if (spTempWebBrowser && m_spWebBrowser2 && m_spWebBrowser2.IsEqualObject(spTempWebBrowser))
		return true;
	return false;
}

// set the window event handler to catch OnLoad for refreshes
void CButtonDemoBHO::setWindowHandler(){
	HRESULT hr;
	CComPtr<IDispatch> spDisp;
	hr=m_spWebBrowser2->get_Document(&spDisp);
	if (SUCCEEDED(hr) && spDisp){
		CComQIPtr<IHTMLDocument2> spHtmlDoc=spDisp;
		if (spHtmlDoc){
			CComPtr<IHTMLWindow2> spWin;
			hr=spHtmlDoc->get_parentWindow(&spWin);
			if (SUCCEEDED(hr) && spWin){
				m_htmlWindowEventSink=new CHtmlWindowEventSink(this);
				m_htmlWindowEventSink->advise(spWin);
			}
		}
	}
}

///////////////////////////////////////////////////////////////////////////
// IOleCommandTarget function
///////////////////////////////////////////////////////////////////////////
STDMETHODIMP CButtonDemoBHO::QueryStatus(const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText){
	if (!cCmds)
		return E_INVALIDARG;
	if (!prgCmds)
		return E_POINTER;

	prgCmds[0].cmdf = OLECMDF_ENABLED;
	return S_OK;
}

// This is called by the IE when the button is pressed
// Also called by the other methods in the BHO when we need to cross a thread boundary
STDMETHODIMP CButtonDemoBHO::Exec(const GUID*, DWORD nCmdID, DWORD d, VARIANTARG*, VARIANTARG* pvaOut){
	ATLTRACE("CButtonDemoBHO::Exec\n");
	switch (nCmdID){
		case BUTTON_PRESSED:
			MessageBox(m_hWnd, L"You have pressed the button", L"Button Pressed", MB_OK);
			break;
		case MENU_ITEM_SELECT:
			MessageBox(m_hWnd, L"You have simulated a button press with the menu ", L"Menu Pressed", MB_OK);
			break;
	}
	return S_OK;
}

///////////////////////////////////////////////////////////////////////////
// Button callback
///////////////////////////////////////////////////////////////////////////

// Thread safe way to call the button. This is over the top for this demo
// but demonstrates it in practice
void CButtonDemoBHO::buttonPressed(){
	// Look up the interface to the BHO in the GIT
	CComPtr<IOleCommandTarget> spTarget;
	HRESULT hr=m_spTable->GetInterfaceFromGlobal(m_interfaceTableCookie, IID_IOleCommandTarget, (void**)&spTarget);
	if (SUCCEEDED(hr))
		// Call the BHO to say the button has been pressed
		spTarget->Exec(NULL, BUTTON_PRESSED, 0, NULL, NULL);	// pass whatever values you need in here
}

// As above but from the menu call
void CButtonDemoBHO::menuPressed(){
	// Look up the interface to the BHO in the GIT
	CComPtr<IOleCommandTarget> spTarget;
	HRESULT hr=m_spTable->GetInterfaceFromGlobal(m_interfaceTableCookie, IID_IOleCommandTarget, (void**)&spTarget);
	if (SUCCEEDED(hr))
		// Call the BHO to say the button has been pressed
		spTarget->Exec(NULL, MENU_ITEM_SELECT, 0, NULL, NULL);	// pass whatever values you need in here
}